Node.js のログを Fluentd を使って MongoDB に集約する

Node.js のログを Fluentd を使って MongoDB に集約する

Clock Icon2013.01.29

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

最近 Fluentd をプロジェクトで導入しようと考えています。

Fluentd は、あらゆるデータのログを json を使ってシンプルに転送、集約させることができるツールです。
クラウド環境やビッグデータを扱う上で、今後需要が高くなりそうです。
ソーシャルゲーム業界などではかなり実績があるみたいですね。

今回お試しとして、Node.js のエラーログを Fluentd を使って MongoDB に保存するサンプルを作ってみます。
こんな感じ。とてもシンプルです。。

    Input                              Output
+------------------------------------------------+
|                                                |
|  Node.js --------> Fluentd --------> MongoDB   |
|                                                |
+------------------------------------------------+

インストール

パッケージ形式の td-agent と gem 形式の Fluentd があるみたいですが、
今回はお試しなので、gem を使ってインストールしてみました。(ちなみに Mac OS 10.8 です)
大規模環境では td-agent を使う方がいいと思います。
Ruby の1.9.2以上が必要です。

 
$ gem install fluentd --no-ri --no-rdoc

起動確認

マニュアルに書いてある通りやってみます。
セットアップ

$ fluentd --setup ./fluent
Installed ./fluent/fluent.conf.

起動

$ fluentd -c ./fluent/fluent.conf -vv &

すんなり起動しました。

MongoDB Output Plugin をインストール

Fluentd はプラグインが豊富です。
今回は、MongoDB の プラグインを fluent-gem というのを使ってインストールします。(td-agentの場合は入っている)

$ fluent-gem install fluent-plugin-mongo

ログの設定

MongoDB のプラグインをインストールしたら、fluent.conf に追記します。

# Single MongoDB
<match mongo.**>
  type mongo
  host localhost
  port 27017
  database node
  collection error

  # for capped collection
  capped
  capped_size 1024m

  # flush
  flush_interval 10s
</match>

match mongo.** で受け取るタグを正規表現で指定できます。
この場合、mongo、mongo.x、mongo.x.x... の条件にマッチします。
type 〜 collection は、MongoDB の接続先と保存先のコレクションです。
capped collection というのは、MongoDB のサイズ固定のコレクションです。
これはロギング処理に向いていますので、Fluentd ではこれが推奨されています。
10秒間隔で MongoDB にフラッシュします。

Logger のインストール(Node.js)

続いて、Node.js 側です。
ちなみに express を使ってます。
以下のようにスケルトンを作成したら、package.json を編集します。

express fluentd-sample

「package.json」

{
  "name": "application-name",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app"
  },
  "dependencies": {
    "express": "3.0.3",
    "fluent-logger": "latest",
    "jade": "*"
  }
}

fluentd の Logger である fluent-logger を使う指定をします。
続けて、npm start。

$ npm start
fluent-logger@0.2.1 node_modules/fluent-logger
└── msgpack@0.1.7

0.2.1がインストールされました。

例外処理のコード(Node.js)

例外処理を記述します。
ちなみに CoffeeScript です。

「app.coffee」

'use strict'

###
Module dependencies.
###

express = require 'express'
routes = require './routes'
path = require 'path'

logger = require 'fluent-logger'
logger.configure 'mongo', {host: 'localhost', port: 24224}

app.configure ->
  app.set 'port', process.env.PORT or 3000
  app.set 'views', __dirname + '/views'
  app.set 'view engine', 'jade'
  app.use express.favicon()
  app.use express.logger 'dev'
  app.use express.bodyParser()
  app.use express.methodOverride()
  app.use app.router
  app.use express.static path.join(__dirname, 'public')
  app.use (err, req, res, next) ->
    logger.emit 'test', {error: err.message}
    res.render 'error', {status: 500, title: '500 Internal Server Error', err: err}

app.get '/', routes.index

app.listen process.env.PORT || 3000, ->
  console.log 'Express server listening on port ' + app.get 'port'

エラーをハンドリングしたら、Fluentd にログを飛ばし、その後エラー画面に遷移させています。
その際のタグとラベルの組み合わせが mongo.test と指定しているので、先ほどの fluent.conf の構文にマッチするというわけです。

routes/index では、テスト的にわざとエラーを発生させるようにします。

「routes/index.coffee」

'use strict'

###
Module dependencies.
###

exports.index = (req, res, next) ->
  next new Error('fluentd test')

エラーを発生させる

Fluentd を起動しておいて、MongoDB も起動します。

$ sudo mongod

そして Node.js を起動し、curl でアクセスします。

$ node app.js 
$ curl localhost:3000/
<!DOCTYPE html><html><head><title>500 Internal Server Error</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>500 Internal Server Error</h1><h1>Error: Fluentd test</h1></body></html>

ログの確認

ちょっと待って MongoDB を確認してみます。

$ mongo
$ use node
$ db.error.find()
{ "_id" : ObjectId("5105183acaf52f08dd00000e"), "error" : "Fluentd test", "time" : ISODate("2013-01-27T12:06:15.513Z") }

ちゃんと出力されてますね。

まとめ

Fluentd と MongoDB はデータを json 形式で扱うため、高いスループットでの挿入が実現できます。
(もちろん Node.js も JavaScript なので json は親和性が高いです)
MongoDB の capped コレクションに保存すると、古いデータが消えていってしまうので、同時にS3にも保存した方がいいかもしれません。
同時にS3にも保存する方法は、以下のブログに書いてあります。
Amazon LinuxにFluentdをインストールしてS3とMongoDB連携する

あとは定期的に Amazon Glacier にアーカイブするなどもいいですね。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.